// |**| Opcon|OperationMango|**|Opcon|OperationMango|**|
// |**| Source Code for the FPS Operation Mango     |**|
// |**| Opcon|OperationMango|**|Opcon|OperationMango|**|

using OpenTK;
using System;
using System.Drawing;
using System.Windows.Forms;
using OpenTK.Input;

namespace OperationMango
{


	
	
	public class QuaternionCamera : ICamera
	{
		#region Constructors
		
		/// <summary>
		/// Creates a new Quaternion Camera
		/// </summary>
		/// <param name="moused">
		/// A <see cref="MouseDevice"/> reference from the current game
		/// </param>
		/// <param name="keyd">
		/// A <see cref="KeyboardDevice"/> reference from the current game
		/// </param>
		/// <param name="bounds">
		/// A <see cref="Rectangle"/> 
		/// </param>
		/// <param name="client">
		/// A <see cref="Rectangle"/>
		/// </param>
		/// <param name="m">
		/// A <see cref="System.Boolean"/> indicating wether mouse look is enabled
		/// </param>
		/// <param name="game">
		/// A <see cref="INativeWindow"/> reference to the current game window
		/// </param>
        public QuaternionCamera(MouseDevice moused, KeyboardDevice keyd, bool m, INativeWindow game)
            : this(new Vector3(), new Quaternion() { X = 0, Y = 0, Z = 1, W = 0 }, moused, keyd, m, game)
        { }
 
        public QuaternionCamera(Vector3 position, MouseDevice moused,
		                        KeyboardDevice keyd, bool m, INativeWindow game)
            : this(position, new Quaternion() { X = 0, Y = 0, Z = 0, W = 0 }, moused, keyd, m, game)
        { }
 
        public QuaternionCamera(Vector3 position, Quaternion orientation, MouseDevice moused,
		                        KeyboardDevice keyd, bool m, INativeWindow game)
        {
            TargetPosition = Position = position;
            TargetOrientation = Orientation = orientation;
			m_Mouse = moused;
			m_Keyboard = keyd;
			m_ParentGame = game;
			
			m_MouseRotation = new Vector2(0,0);
			
			MouseLookEnabled = m;
 
            m_AspectRatio = 1.333f;
            m_FieldOfView = 60;
            m_ZNear = 0.1f;
            m_ZFar = 64;
						
			if (MouseLookEnabled)
			{	
				Cursor.Hide();
				ResetMouse();
				ApplyRotation();
				Orientation = TargetOrientation;
				Console.WriteLine("Mouse XY = {0},{1}, Window Center = {2}, Cursor.Positon = {3}, Orientation = {4}, Target Orientation = {5}.", m_Mouse.X, m_Mouse.Y, WindowCenter, Cursor.Position, Orientation, TargetOrientation);
			}
        }
 
        #endregion
 
        #region Members
 
        #region Properties
 
		protected Vector3 m_Position;
        public Vector3 Position { get { return m_Position; } set { m_Position = value; } }
		protected Quaternion m_Orientation;
        public Quaternion Orientation { get { return m_Orientation; } set { m_Orientation = value; } }
 
		protected Vector3 m_TargetPosition;
        public Vector3 TargetPosition { get { return m_TargetPosition; } set { m_TargetPosition = value; } }
		protected Quaternion m_TargetOrientation;
        public Quaternion TargetOrientation { get { return m_TargetOrientation; } set { m_TargetOrientation = value; } }
		
        public Vector3 Forward { get { return MathExtensions.Apply( Orientation, Vector3.UnitZ); } }
        public Vector3 Right { get { return MathExtensions.Apply( Orientation, Vector3.UnitX); } }
        public Vector3 Up { get { return MathExtensions.Apply( Orientation, Vector3.UnitY); } }

		protected bool m_MouseLookEnabled;
		public bool MouseLookEnabled 
		{ get { return m_MouseLookEnabled; } set {  m_MouseLookEnabled = value; if (MouseLookEnabled) {Cursor.Hide(); ResetMouse();} else Cursor.Show(); } }
		
		protected float m_MouseYSensitivity;
		public float MouseYSensitivity { get { return m_MouseYSensitivity; } set { m_MouseYSensitivity = value; } }
		protected float m_MouseXSensitivity;
		public float MouseXSensitivity { get { return m_MouseXSensitivity; } set { m_MouseXSensitivity = value; } }
		
		protected Vector2 m_MouseRotation;
		public Vector2 MouseRotation { get { return m_MouseRotation; } set { m_MouseRotation = value; } }
		protected Vector3 m_Movement;
		public Vector3 Movement { get { return m_Movement; } set { m_Movement = value; } }
		
		protected float m_MaxSpeed;
		public float MaxSpeed { get { return m_MaxSpeed; } set { m_MaxSpeed = value; } }
		protected float m_Acceleration;
		public float Acceleration { get { return m_Acceleration; } set { m_Acceleration = value; } }
		
		protected Point WindowCenter
		{ get { return new Point( (m_ParentGame.ClientRectangle.Left + m_ParentGame.ClientRectangle.Right) / 2, (m_ParentGame.ClientRectangle.Top + m_ParentGame.ClientRectangle.Bottom) / 2); } }
 
		protected float m_ZNear;
		public float ZNear { get { return m_ZNear; } set { m_ZNear = value; } }
		protected float m_ZFar;
		public float ZFar { get { return m_ZFar; } set { m_ZFar = value; } }
		protected float m_FieldOfView;
		public float FieldOfView { get { return m_FieldOfView; } set { m_FieldOfView = value; } }
		protected float m_AspectRatio;
		public float AspectRatio { get { return m_AspectRatio; } set { m_AspectRatio = value; } }
		
		protected CamMode m_CameraMode;
		public CamMode CameraMode 
		{
			get 
			{
				return m_CameraMode;
			}
		}
		
		#endregion
		
		#region Protected Variables
		
		/// <summary>
		/// The current Mouse Delta (How much the mouse has moved since the last frame
		/// </summary>
		protected Point m_MouseDelta;
		
		protected MouseDevice m_Mouse;
		protected KeyboardDevice m_Keyboard;
		
		protected INativeWindow m_ParentGame;
		
		
		#endregion
 
        #region Public Methods
 
        public void Update(double time)
        {
			if (MouseLookEnabled)
			{
				UpdateRotations(time);
				Console.WriteLine ("m_MouseRotation={0}", MouseRotation);
			}
			UpdateMovement(time);
			
            if (time == 0)
                return;
 
            if (TargetOrientation != Orientation)
            {
                Orientation = Quaternion.Slerp(Orientation, TargetOrientation, (float)time);
            }

            if (TargetPosition != Position)
            {
                Position = Vector3.Lerp(Position, TargetPosition, (float)time);
            }
			
        }

        public void GetProjectionMatrix(out Matrix4 matrix)
        {
            matrix = Matrix4.CreatePerspectiveFieldOfView((float)(m_FieldOfView * Math.PI / 180.0), m_AspectRatio, m_ZNear, m_ZFar);
        }

        public void GetModelviewProjectionMatrix(out Matrix4 result)
        {
            Matrix4 modelview;
            GetProjectionMatrix(out result);
            GetModelviewMatrix(out modelview);
            Matrix4.Mult(ref modelview, ref result, out result);
        }
 
        public void GetModelviewMatrix(out Matrix4 matrix)
        {
            var translation_matrix = Matrix4.CreateTranslation(-Position);
            var rotation_matrix = Matrix4.Rotate(Orientation);
            Matrix4.Mult(ref translation_matrix, ref rotation_matrix, out matrix);
        }
		
		/// <summary>
		/// Updates the Parent game object used for window calculations
		/// </summary>
		/// <param name="game">
		/// A <see cref="INativeWindow"/>
		/// </param>
		public void UpdateParentGame(INativeWindow game)
		{
			m_ParentGame = game;
		}
			
		/// <summary>
		/// Sets up this camera with the specified Camera Mode
		/// </summary>
		/// <param name="mode">
		/// A <see cref="CamMode"/>
		/// </param>
		public void SetCameraMode(CamMode mode)
		{
			m_CameraMode = mode;
		}
		
		public void FixMouse()
		{
			ResetMouse();
			CalculateMouseDelta();
			ApplyRotation();
			Orientation = TargetOrientation;
		}
		
		#endregion
		
		#region Protected Methods
		
		/// <summary>
		/// Calculates the Mouse Delta (How far it has moved from the last update) from the center of the screen
		/// </summary>
		protected void CalculateMouseDelta()
		{
			Point m_MouseCurrent;
			if (m_Mouse.X == 0 || m_Mouse.Y == 0)
				m_MouseCurrent = WindowCenter;
			else
				m_MouseCurrent = new Point(m_Mouse.X, m_Mouse.Y);
			
		    m_MouseDelta = new Point(
		        m_MouseCurrent.X - WindowCenter.X,
		        m_MouseCurrent.Y - WindowCenter.Y);
		}
		
		/// <summary>
		/// Resets the mouse to the center of the screen
		/// </summary>
		protected void ResetMouse ()
		{
			if (m_ParentGame.WindowState == WindowState.Fullscreen)
			{
				Cursor.Position = WindowCenter;
			}
				
			else
			{
				Cursor.Position = m_ParentGame.PointToScreen(WindowCenter);
			}
			

		}
		
		/// <summary>
		/// Clamps the mouse rotation values
		/// </summary>
		protected void ClampMouseValues ()
		{
			if (m_MouseRotation.Y >= 1.57)
				m_MouseRotation.Y = 1.57f;
			if (m_MouseRotation.Y <= -1.57)
				m_MouseRotation.Y = -1.57f;
			
			if (m_MouseRotation.X >= 6.28)
				m_MouseRotation.X -= 6.28f;
			if (m_MouseRotation.X <= -6.28)
				m_MouseRotation.X += 6.28f;
		}
		
		/// <summary>
		/// Updates the Orientation Quaternion for this camera using the calculated Mouse Delta
		/// </summary>
		/// <param name="time">
		/// A <see cref="System.Double"/> containing the time since the last update
		/// </param>
		protected void UpdateRotations (double time)
		{
			CalculateMouseDelta();
			m_MouseRotation.X += (float)(m_MouseDelta.X * MouseXSensitivity * time);
			m_MouseRotation.Y += (float)(m_MouseDelta.Y * MouseYSensitivity * time);
			
			ClampMouseValues();
			ApplyRotation ();
			if (CameraMode != CamMode.FlightCamera) 
			{
				Orientation = TargetOrientation;
			}

			ResetMouse();
		}
		
		protected void ApplyRotation ()
		{
			TargetOrientation = Quaternion.FromAxisAngle(Vector3.UnitX, MouseRotation.Y) *
			Quaternion.FromAxisAngle(Vector3.UnitY, ((MathHelper.Pi + MouseRotation.X)));
			
		}

		/// <summary>
		/// Updates the Position vector for this camera
		/// </summary>
		/// <param name="time">
		/// A <see cref="System.Double"/> containing the time since the last update
		/// </param>
		protected void UpdateMovement (double time)
		{
			if (m_Keyboard[Key.W] && !m_Keyboard[Key.S])
			{
				m_Movement.Z = 0;
				m_Movement.Z -= 50.0f * (float)time;
			}
			else if (m_Keyboard[Key.S] && !m_Keyboard[Key.W])
			{
				m_Movement.Z = 0;
				m_Movement.Z += 50.0f * (float)time;
			}
			else m_Movement.Z = 0.0f;
			
			if (m_Keyboard[Key.A] && !m_Keyboard[Key.D])
			{
				m_Movement.X = 0.0f;
				m_Movement.X -= 50.0f * (float)time;
			}
			else if (m_Keyboard[Key.D] && !m_Keyboard[Key.A])
			{
				m_Movement.X = 0.0f;
				m_Movement.X += 50.0f * (float)time;
			}
			else
				m_Movement.X = 0.0f;
			
			TargetPosition += Vector3.Transform(Movement, Quaternion.Invert(Orientation));
			if (CameraMode != CamMode.FlightCamera)
			Position = TargetPosition;
			Console.WriteLine ("Position={0}", Position);
		}
 
        #endregion
 
        #endregion

		
		


	}
}
